/******************************************************************************/
/*                                                                            */
/*  Copyright (c) 2009: ngi-central.org                                       */
/*  All rights reserved.                                                      */
/*                                                                            */
/******************************************************************************/


/// \file
/// This file implements the triangle geometric primitive.

#ifndef __sgl_triangle_h__
#define __sgl_triangle_h__

#include <sgl/global_settings.h>
#include <sgl/conditions.h>
#include <sgl/geometric_primitive.h>
#include <sgl/point.h>
#include <boost/static_assert.hpp>
#include <boost/operators.hpp>
#include <boost/concept_check.hpp>

namespace sgl {


/******************************************************************************/
/*                                                                            */
/* triangle                                                                   */
/*                                                                            */
/******************************************************************************/

template <
	/// The dimensionality. 
	//!
	//! This defaults to two dimensions.
	size_t D=2,
	/// The element type. 
	//! 
	//! This type defaults to SGL_FPTYPE (which in turn defaults to double), 
	//! but any other numeric type is also suitable.
	typename T=SGL_FPTYPE>
/// A triangle in euclidean space.
//!
//! A triangle represents a polygon with 3 vertices.
class triangle
	/// A triangle is a geometric primitive.
	: public geometric_primitive<D,T>
	, boost::equality_comparable<sgl::triangle<D,T>, sgl::triangle<D,T>
	, boost::additive<sgl::triangle<D,T>, sgl::vector<D,T>
	> >
{
	// A triangle must have two or more dimensions.
	BOOST_STATIC_ASSERT(D >= 2);

public:
	/// Default constructor.
	//!
	//! Constructs a zero triangle.
	triangle()
		: p1_()
		, p2_()
		, p3_()
	{ }

	/// Construct a triangle from three points.
	triangle(
		/// The first point of the triangle.
		sgl::point<D,T> p1,
		/// The second point of the triangle.
		sgl::point<D,T> p2,
		/// The third point of the triangle.
		sgl::point<D,T> p3)
		: p1_(p1)
		, p2_(p2)
		, p3_(p3)
	{ }

	/// Copy constructor.
	triangle(
		/// The right hand side.
		sgl::triangle<D,T> const& rhs)
		: p1_(rhs.p1_)
		, p2_(rhs.p2_)
		, p3_(rhs.p3_)
	{ }

	/// Copy assignment operator.
	sgl::triangle<D,T> & operator = (
		/// The right hand side.
		sgl::triangle<D,T> rhs)
	{
		swap(rhs);
		return *this;
	}

	/// Exchanges the elements of two triangle objects.
	void swap(
		/// The other triangle.
		sgl::triangle<D,T> & rhs)
	{
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		std::swap(p1_, rhs.p1_);
		std::swap(p2_, rhs.p2_);
		std::swap(p3_, rhs.p3_);
	}

	/// Find the type of the geometric primitive.
	//!
	//! /return The type of the primitive.
	geometric_type type() const
	{
		return sgl::geometric_primitive<D,T>::triangle;
	} 

	/// Access the first point of the triangle.
	//!
	//! /return Returns a reference to the first point of the triangle.
	sgl::point<D,T> & p1()
	{
		return p1_;
	}

	/// Access the first point of the triangle.
	//!
	//! /return Returns a const reference to the first point of the triangle.
	sgl::point<D,T> const & p1() const
	{
		return p1_;
	}

	/// Access the second point of the triangle.
	//!
	//! /return Returns a reference to the second point of the triangle.
	sgl::point<D,T> & p2()
	{
		return p2_;
	}

	/// Access the second point of the triangle.
	//!
	//! /return Returns a const reference to the second point of the triangle.
	sgl::point<D,T> const & p2() const
	{
		return p2_;
	}

	/// Access the third point of the triangle.
	//!
	//! /return Returns a reference to the third point of the triangle.
	sgl::point<D,T> & p3()
	{
		return p3_;
	}

	/// Access the third point of the triangle.
	//!
	//! /return Returns a const reference to the third point of the triangle.
	sgl::point<D,T> const & p3() const
	{
		return p3_;
	}

	/// Equality operator.
	//!
	//! Two triangles are compared for equality by comparing their vertices in a 
	//! pairwise manner. The triangles are equal only if all pairwise 
	//! comparisons are equal.
	//!
	//! Through the use of boost::equality as a base class operator != is 
	//! defined as well and implemented in terms of this operator ==.
	//!
	//! \return true if equal, false otherwise.
    bool operator == (
		/// Right hand side.
		sgl::triangle<D,T> const& rhs) const
    {
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		return 
			p1_ == rhs.p1_ &&
			p2_ == rhs.p2_ &&
			p3_ == rhs.p3_;
    }

	/// Add a vector to a triangle.
	//!
	//! Adding a vector to a triangle moves the triangle in direction of the 
	//! vector. Addition is implemented by pairwise addition of the components 
	//! of the triangles vertices and the vector.
	//!
	//! Through the use of boost::additive as a base class operator + is 
	//! defined as well and implemented in terms of this operator +=.
	//!
	//! \return A reference to this object.
    sgl::triangle<D,T> & operator += (
		/// Right hand side.
		sgl::vector<D,T> const& rhs)
    {
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		p1_ += rhs;
		p2_ += rhs;
		p3_ += rhs;

        return *this;
    }

	/// Subtract a vector from a triangle.
	//!
	//! Subtracting a vector from a triangle moves the triangle in the opposite 
	//! direction of the vector. Subtraction is implemented by pairwise 
	//! subtraction of the components of the triangles vertices and the vector.
	//!
	//! Through the use of boost::additive as a base class operator - is 
	//! defined as well and implemented in terms of this operator -=.
	//!
	//! \return A reference to this object.
    sgl::triangle<D,T> & operator -= (
		/// Right hand side.
		sgl::vector<D,T> const& rhs)
    {
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		p1_ -= rhs;
		p2_ -= rhs;
		p3_ -= rhs;

		return *this;
    }

private:
	// The first point of the triangle.
	sgl::point<D,T> p1_;

	// The second point of the triangle.
	sgl::point<D,T> p2_;

	// The third point of the triangle.
	sgl::point<D,T> p3_;
};


/// A two-dimensional triangle with the default component type.
typedef sgl::triangle<2> triangle2;

/// A two-dimensional triangle with float component type.
typedef sgl::triangle<2, float> triangle2f;

/// A two-dimensional triangle with double component type.
typedef sgl::triangle<2, double> triangle2d;


/// A three-dimensional triangle with the default component type.
typedef sgl::triangle<3> triangle3;

/// A three-dimensional triangle with float component type.
typedef sgl::triangle<3, float> triangle3f;

/// A three-dimensional triangle with double component type.
typedef sgl::triangle<3, double> triangle3d;


} // namespace sgl

#endif // __sgl_triangle_h__
